package com.urea.system.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.urea.common.core.domain.AjaxResult;
import com.urea.common.core.domain.entity.Customer;
import com.urea.common.exception.ServiceException;
import com.urea.common.utils.SecurityUtils;
import com.urea.common.utils.StringUtils;
import com.urea.common.utils.sign.EncryptUtils;
import com.urea.common.utils.uuid.IdUtils;
import com.urea.system.domain.*;
import com.urea.system.domain.vo.CustomerAccountVo;
import com.urea.system.domain.vo.OrderVo;
import com.urea.system.domain.vo.PayVo;
import com.urea.system.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
import java.util.concurrent.ScheduledExecutorService;

import static com.urea.system.constants.BusinessConstants.*;

/**
 * 类描述:
 *
 * @author fangXu
 * @version 1.0
 * @date 2023/8/22 17:25
 */
@Service
@Slf4j
public class PayServiceImpl implements IPayService {

    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private IOrderService orderService;
    @Autowired
    private IPayInfoService payInfoService;
    @Autowired
    private ICustomerAccountDetailService accountDetailService;
    @Autowired
    private ICustomerService customerService;
    @Autowired
    private ICustomerAccountService accountService;
    @Autowired
    private IRechargePackageService packageService;
    @Autowired
    private ICustomerAccountService customerAccountService;
    @Autowired
    private IAgentTerminalService agentTerminalService;
    @Autowired
    private IFleetTerminalService fleetTerminalService;
    @Autowired
    private IHardwareService hardwareService;
    @Autowired
    private ITerminalConfigService terminalConfigService;
    @Autowired
    private ITerminalService terminalService;
    @Autowired
    private ITerminalCommLogService terminalCommLogService;
    @Autowired
    private IOrderRefundService orderRefundService;
    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private ISysConfigService configService;
    @Value("${pay.aesKey}")
    private String aesKey;
    @Value("${pay.publicKey}")
    private String publicKey;
    @Value("${pay.parentId}")
    private String parentId;
    @Value("${wx.merchantId}")
    private String merchantId;
    @Value("${wx.delayedSettleType}")
    private String delayedSettleType;
    @Value("${wx.miniapp.configs[0].appid}")
    private String appid;
    @Value("${localhost.url}")
    private String url;


    //todo 定时任务查询订单支付状态
    @Override
    public JSONObject payToWx(OrderVo vo) {
        // 拼装参数
        JSONObject param = new JSONObject();
        //商户号
        param.put("merchantId", merchantId);
        //外部订单号，商户下唯一
        param.put("payOrderNo", vo.getOrderNo());
        //交易金额，单位为元，保留两位小数，取值范围[0.01,100000000]
        param.put("transAmount", vo.getPayAmount().setScale(2, RoundingMode.HALF_UP));
        //交易类型：1-微信；2-支付宝；3-云闪付
        param.put("transType", "1");
        //订单类型：2-native，原生扫码支付（支持支付宝、银联二维码）；3-JSAPI，公众号支付，服务窗支付，H5拉起支付；4-小程序支付（微信）；
        param.put("payType", "4");
        //付款用户id，“微信sub_openid”，“支付宝 user_id”，“云闪付user_id”，
        Customer customerUser = SecurityUtils.getLoginUser().getCustomerUser();
        if (Objects.isNull(customerUser)) {
            throw new ServiceException("用户信息为空");
        }
        param.put("subOpenid", customerUser.getOpenId());
        //订单标题。示例：Iphone 16G
        param.put("subject", "充值/加注/续费");
        //商品描述 todo 待修改
        param.put("body", "尿素机充值/加注/续费");
        //支付成功后回调地址，如果不填会用代理商默认的支付回调地址
        param.put("notifyUrl", url.concat("api/wx/pay/paySuccess"));
        //终端设备号，由代理商自行定义
        param.put("deviceInfo", vo.getTerminalId());
        // 是否延迟结算订单：0-否；1-是；
        param.put("delayedSettleType", delayedSettleType);
        //附加数据，在查询 API 和支付通知中原样返回，可作为自定义参数使用，实际情况下只有支付完成状态才会返回该字段。
        param.put("attach", JSONObject.from(vo.getParams()).toJSONString());
        // 订单失效时间 格式：yyyyMMddHHmmss
//        param.put("timeExpire","");
        //subAppid	String	O	32	微信公众号ID ；微信公众号支付的公众号id或微信小程序支付的小程序appid;
        param.put("subAppid", appid);
        //小程序支付成功后的跳转地址 //微信公众号支付成功后跳转路径须在微信平台点金计划中配置
//        param.put("callbackUrl","pages/fill/fillOver");
        //-	公众号、小程序支付过程中用户取消支付或支付失败跳转地址
//        param.put("cancelUrl","");
        // 加密
        String data = EncryptUtils.encryptData(param.toJSONString(), aesKey);
        String paramData = handelData(data);
        // 加签
        String sign = EncryptUtils.sign(paramData, true);
        JSONObject finalData = JSONObject.parse(paramData);
        finalData.put("sign", sign);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity(finalData.toJSONString(), headers);
        log.info("###########请求三方支付加密参数:{},原始参数:{}#################", paramData, param.toJSONString());
        // 发送请求
        String s = restTemplate.postForObject("https://eapi.lianfutech.cn/api/v1/pay/unifyPay", entity, String.class);
        log.info("###########请求三方支付结果:{}#################", s);
        // 处理响应信息
        return doCheckSignAndDecryptData(s);
    }


    /**
     * 接收支付成功通知
     *
     * @param json 通知信息
     * @return 响应信息
     */
    @Override
    @Transactional
    public JSONObject paySuccess(JSONObject json) {

        log.info("############接收到支付成功通知:{}##################", json.toString());
        // 验签
        String sign = (String) json.remove("sign");
        EncryptUtils.checkSign(json.toString(), sign, true, publicKey);
        // 解密
        String data = EncryptUtils.decryptData(json.getString("data"), aesKey);
        log.info("############支付成功通知解密后参数:{}##################", data);
        JSONObject param = JSONObject.parse(data);
        // 更新訂單信息
        String orderNo = param.getString("payOrderNo");
        try {
            // 主动查询订单信息
            JSONObject jsonObject = queryOrderStatusToWx(orderNo);
            Order order = orderService.getOne(Wrappers.lambdaQuery(Order.class).eq(Order::getOrderNo, orderNo));
            if (Objects.isNull(order)) {
                throw new ServiceException("订单不存在");
            }
            if (order.getStatus().equals(SUCCESS_PAY)) {
                // 订单状态已修改 直接返回
                return getReturnInfoToWx();
            }
            Order update = new Order();
            update.setId(order.getId());
            //订单状态：3-支付中；4-支付成功；5-支付失败
            if ("4".equals(param.getString("orderStatus"))) {
                update.setStatus(SUCCESS_PAY);
                update.setPayTime(param.getObject("payTime", LocalDateTime.class));
                orderService.updateById(update);
            }
            if ("5".equals(jsonObject.getString("orderStatus"))) {
                update.setStatus(FAIL_PAY);
                orderService.updateById(update);
                log.info("########订单:{}支付失败###########", orderNo);
                return getReturnInfoToWx();
            }
            // 更新详细信息
            PayInfo payInfo = payInfoService.getOne(Wrappers.lambdaQuery(PayInfo.class).eq(PayInfo::getOrderNo, orderNo));
            if (Objects.isNull(payInfo)) {
                throw new ServiceException("订单详细信息不存在");
            }
            PayInfo updatePay = new PayInfo();
            updatePay.setId(payInfo.getId());
            updatePay.setOutPayNo(param.getString("orderNo"));
            updatePay.setPayTime(param.getObject("payTime", LocalDateTime.class));
            updatePay.setPayAmount(param.getBigDecimal("actualAmount"));
            updatePay.setPayStatus(SUCCESS_PAY);
            payInfoService.updateById(updatePay);

            String openid = param.getString("subOpenid");
            Customer user = customerService.getOne(Wrappers.lambdaQuery(Customer.class).eq(Customer::getOpenId, openid));
            if (Objects.isNull(user)) {
                throw new ServiceException("用户信息为空");
            }
            CustomerAccount account = accountService.getOne(Wrappers.lambdaQuery(CustomerAccount.class)
                    .eq(CustomerAccount::getCustomerId, user.getId()));
            // 充值
            CustomerAccount accountUpdate = new CustomerAccount();
            if (Objects.equals(payInfo.getPayMethod(), BIZ_RECHARGE)) {
                JSONObject attach = JSONObject.parse(param.getString("attach"));
                // 如果存在赠送金额
                BigDecimal payAmount = param.getBigDecimal("actualAmount");
                if (Objects.nonNull(attach)) {
                    BigDecimal giftAmount = attach.getBigDecimal("giftAmount");
                    if (Objects.nonNull(giftAmount)) {
                        payAmount = payAmount.add(giftAmount);
                    }
                }
                if (Objects.nonNull(account)) {
                    accountUpdate.setId(account.getId());
                    BigDecimal actualAmount = account.getBalance().add(payAmount);
                    accountUpdate.setBalance(actualAmount);
                    // 增加余额
                    accountService.updateById(accountUpdate);
                } else {
                    accountUpdate.setBalance(payAmount);
                    accountUpdate.setCustomerId(user.getId());
                    accountService.save(accountUpdate);
                }
                // 升级会员
                if (user.getUserType().equals(0)) {
                    customerService.update(Wrappers.lambdaUpdate(Customer.class).eq(Customer::getId, user.getId())
                            .set(Customer::getUserType, 1));
                }
            } else if (Objects.equals(payInfo.getPayMethod(), BIZ_RENEW)) {
                terminalService.renew(order);

            } else {
                // 加注 发送配置命令  在接收回复中发送出货指令
                TerminalConfig config = terminalConfigService.getByTerminalId(order.getTerminalId());
                Terminal terminal = terminalService.getById(config.getTerminalId());
                doRunTerminal(terminal, config, null);
            }
            // 创建用户流水
            ScheduledExecutorService threadPool = SpringUtil.getBean("scheduledExecutorService");
            threadPool.execute(() -> {
                CustomerAccountDetail detail = new CustomerAccountDetail();
                if (Objects.nonNull(accountUpdate.getId())) {
                    detail.setRefAccount(accountUpdate.getId());
                    detail.setBalance(accountUpdate.getBalance());
                }
                // 加注支出 充值 退款收入
                detail.setIncome(Objects.equals(payInfo.getPayMethod(), BIZ_FILL) ? "1" : "0");
                detail.setRefUid(user.getId());
                // 客户
                detail.setCustomer(IS_CUSTOMER);
                detail.setAmount(param.getBigDecimal("actualAmount"));
                detail.setType(payInfo.getPayMethod());
                detail.setRefId(orderNo);
                accountDetailService.save(detail);
            });
        } finally {
            // todo 测试需要 加注结束已经解冻订单  上线直接去掉这段逻辑即可
            ScheduledExecutorService threadPool = SpringUtil.getBean("scheduledExecutorService");
            threadPool.execute(() -> thawApply(orderNo));
        }
        return getReturnInfoToWx();
    }

    private void doRunTerminal(Terminal terminal, TerminalConfig config, BigDecimal discount) {

        String price;
        if (Objects.isNull(discount)) {
            price = config.getPrice().toString();
        } else {
            price = discount.multiply(new BigDecimal(config.getPrice().toString())).setScale(0, RoundingMode.DOWN).toString();
        }
        hardwareService.config(price, config.getPulse().toString(), terminal.getModel(),
                config.getTimeout().toString(), terminal.getTerminalNo());
    }


    /**
     * 商户号与小程序绑定
     *
     * @param merchantId 商户号
     * @param appid      小程序id
     */
    @Override
    public void bindWxConf(String merchantId, String appid) {
        JSONObject json = new JSONObject();
        json.put("merchantId", merchantId);
        if (StringUtils.isBlank(merchantId)) {
            json.put("merchantId", this.merchantId);
        }
        json.put("subAppid", appid);
        if (StringUtils.isBlank(appid)) {
            json.put("subAppid", this.appid);
        }
        // 加密
        String s = EncryptUtils.encryptData(json.toJSONString(), aesKey);
        String s1 = handelData(s);
        String sign = EncryptUtils.sign(s1, true);
        JSONObject param = JSONObject.parse(s1);
        param.put("sign", sign);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity(param.toJSONString(), headers);
        log.info("###########请求三方商户号与小程序绑定加密参数:{},原始参数:{}#################", param.toString(), json.toJSONString());
        // 发送请求
        String res = restTemplate.postForObject("https://papi.lianfutech.cn/v1/merchant/wechat/config", entity, String.class);
        log.info("###########请求三方商户号与小程序绑定结果:{}#################", res);
        JSONObject jsonObject = doCheckSignAndDecryptData(res);
    }

    /**
     * 商户延时结算
     *
     * @param merchantId        商户号
     * @param delayedSettleType 延迟结算：0-关闭，1-开启；
     */
    @Override
    public void delayed(String merchantId, String delayedSettleType) {
        JSONObject json = new JSONObject();
        json.put("merchantId", merchantId);
        if (StringUtils.isBlank(merchantId)) {
            json.put("merchantId", this.merchantId);
        }
        json.put("delayedSettleType", delayedSettleType);
        if (StringUtils.isBlank(delayedSettleType)) {
            json.put("delayedSettleType", this.delayedSettleType);
        }
        // 加密
        String s = EncryptUtils.encryptData(json.toJSONString(), aesKey);
        String s1 = handelData(s);
        String sign = EncryptUtils.sign(s1, true);
        JSONObject param = JSONObject.parse(s1);
        param.put("sign", sign);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity(param.toJSONString(), headers);
        log.info("###########请求三方设置延迟结算权限加密参数:{},原始参数:{}#################", param.toString(), json.toJSONString());
        // 发送请求
        String res = restTemplate.postForObject("https://papi.lianfutech.cn/v1/merchant/delayed/settle", entity, String.class);
        log.info("###########请求三方设置延迟结算权限结果:{}#################", res);
        doCheckSignAndDecryptData(res);
    }

    /**
     * 查询微信订单支付状态
     *
     * @param orderNo 订单id
     * @return 响应信息
     */
    @Override
    public JSONObject queryOrderStatusToWx(String orderNo) {
        JSONObject json = new JSONObject();
        json.put("payOrderNo", orderNo);
        json.put("merchantId", merchantId);
        String s = EncryptUtils.encryptData(json.toJSONString(), aesKey);
        String s1 = handelData(s);
        String sign = EncryptUtils.sign(s1, true);
        JSONObject param = JSONObject.parse(s1);
        param.put("sign", sign);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity(param.toJSONString(), headers);
        log.info("###########请求三方查询订单支付状态参数:{},原始参数:{}#################", param.toString(), json.toJSONString());
        // 发送请求
        String res = restTemplate.postForObject("https://eapi.lianfutech.cn/api/v1/pay/order", entity, String.class);
        log.info("###########请求三方查询订单支付状态结果:{}#################", res);
        return doCheckSignAndDecryptData(res);
    }

    /**
     * 充值
     *
     * @param vo 充值信息
     * @return 结果
     */
    @Override
    @Transactional
    public AjaxResult recharge(PayVo vo) {
        OrderVo orderVo = new OrderVo();
        // 订单信息
        Order order = new Order();
        // 获取支付金额
        BigDecimal amount = vo.getRechargeAmount();
        if (Objects.nonNull(vo.getPackageId())) {
            RechargePackage rechargePackage = packageService.getById(vo.getPackageId());
            amount = rechargePackage.getRechargeAmount();
            orderVo.getParams().put("giftAmount", rechargePackage.getGiftAmount());
            order.setOrderDetail(JSONObject.toJSONString(rechargePackage));
        }
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException("请输入正确的金额");
        }
        Long userId = SecurityUtils.getUserId();
        String orderNo = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddhhmmssSSS"));

        // 调三方预支付
        orderVo.setOrderNo(orderNo);
        orderVo.setPayAmount(amount);
        orderVo.setTerminalId(vo.getTId());
        saveOrderInfo(vo, amount, amount, userId, orderNo, WX_PAY, order, ORDER_STATUS_INIT);
        // 支付流水信息
        savePayInfo(amount, userId, orderNo, BIZ_RECHARGE, WAIT_PAY);
        JSONObject jsonObject = this.payToWx(orderVo);
        // 跟新外部订单号
        payInfoService.update(Wrappers.lambdaUpdate(PayInfo.class).eq(PayInfo::getOrderNo, orderNo)
                .set(PayInfo::getOutPayNo, jsonObject.getString("orderNo")));
        String miniPayInfo = jsonObject.getString("miniPayInfo");
        JSONObject result = JSONObject.parse(miniPayInfo);
        result.put("orderId", order.getId());
        return AjaxResult.success(result);
    }

    /**
     * 加注
     *
     * @param vo 加注信息
     * @return 结果
     */
    @Override
    @Transactional
    public AjaxResult pay(PayVo vo) {
        // 获取支付金额
        BigDecimal amount = vo.getRechargeAmount();
        if (Objects.nonNull(vo.getPackageId())) {
            RechargePackage rechargePackage = packageService.getById(vo.getPackageId());
            amount = rechargePackage.getRechargeAmount();
        }
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException("请输入正确的金额");
        }
        TerminalConfig config = terminalConfigService.getByTerminalId(vo.getTId());
        if (Objects.isNull(config)) {
            throw new ServiceException("设备未进行单价系数配置,请联系管理员配置后在进行加注");
        }
        Terminal terminal = terminalService.getById(config.getTerminalId());
        if (Objects.isNull(terminal.getStoreUid())) {
            throw new ServiceException("该终端未绑定门店");
        }
        if (Objects.equals(terminal.getEnable(), NO)) {
            throw new ServiceException("该终端未续费，已停用。请联系门店管理员");
        }
        //是否有正在进行的加注订单
        if (terminalCommLogService.isRefueling(terminal.getTerminalNo(), config.getTimeout())) {
            throw new ServiceException("此设备占用中，请稍后再试");
        }

        Long userId = SecurityUtils.getUserId();
        BigDecimal balance = null;
        BigDecimal payAmount = null;
        Long accountId = null;
        Integer payType = null;
        BigDecimal discount = new BigDecimal(BigInteger.ONE);
        String orderNo = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddhhmmssSSS"));
        // 微信支付
        if (vo.getPayType().equals(WX_PAY)) {
            // 调用预支付.创建订单信息
            OrderVo orderVo = new OrderVo();
            orderVo.setOrderNo(orderNo);
            orderVo.setPayAmount(amount);
            orderVo.setTerminalId(vo.getTId());
            JSONObject jsonObject = this.payToWx(orderVo);
            payAmount = amount;
            payType = WX_PAY;
            // 订单信息
            Order order = new Order();
            saveOrderInfo(vo, amount, payAmount, userId, orderNo, payType, order, ORDER_STATUS_INIT);
            // 支付流水信息
            savePayInfo(payAmount, userId, orderNo, BIZ_FILL, WAIT_PAY);
            String miniPayInfo = jsonObject.getString("miniPayInfo");
            // 更新外部订单号
            payInfoService.update(Wrappers.lambdaUpdate(PayInfo.class).eq(PayInfo::getOrderNo, orderNo)
                    .set(PayInfo::getOutPayNo, jsonObject.getString("orderNo")));
            JSONObject result = JSONObject.parse(miniPayInfo);
            result.put("orderId", order.getId());
            return AjaxResult.success(result);
        }
        // 钱包余额支付
        if (vo.getPayType().equals(WALLET_PAY)) {
            // 查询用户可用余额
            CustomerAccount account = customerAccountService.getOne(Wrappers.lambdaQuery(CustomerAccount.class)
                    .eq(CustomerAccount::getCustomerId, userId));
            if (Objects.isNull(account) || amount.compareTo(account.getBalance()) > 0) {
                throw new ServiceException("钱包余额不足,请先充值!");
            }
            balance = account.getBalance();
            accountId = account.getId();
            payAmount = amount;
            payType = WALLET_PAY;
        } else if (vo.getPayType().equals(FLEET_PAY)) {
            // 获取当前车队队长用户余额
            CustomerAccountVo fleetAccount = customerAccountService.getFleetBalanceByUid(userId);
            if (Objects.isNull(fleetAccount)) {
                throw new ServiceException("车队信息异常!");
            }
            // 获取车队折扣率
            FleetTerminal fleetTerminal = fleetTerminalService.getOne(Wrappers.lambdaQuery(FleetTerminal.class)
                    .eq(FleetTerminal::getFleetId, fleetAccount.getFleetId())
                    .eq(FleetTerminal::getTerminalId, vo.getTId()));
            if (Objects.nonNull(fleetTerminal)) {
                discount = fleetTerminal.getDiscount();
            }
            // 查询用户可用余额
            balance = fleetAccount.getBalance();
            if (amount.compareTo(balance) > 0) {
                throw new ServiceException("钱包余额不足,请先充值!");
            }
            payAmount = amount;
            accountId = fleetAccount.getId();
            payType = FLEET_PAY;
        }
        // 更新钱包余额信息
        BigDecimal newBalance = balance.subtract(amount);
        customerAccountService.updateBalanceById(newBalance, accountId);
        // 新建用户流水信息
        saveAccountDetail(amount, accountId, newBalance, orderNo, userId);
        // 订单信息
        Order order = new Order();
        saveOrderInfo(vo, amount, payAmount, userId, orderNo, payType, order, ORDER_STATUS_COMPLETE);
        // 支付流水信息
        savePayInfo(payAmount, userId, orderNo, BIZ_FILL, SUCCESS_PAY);
        // todo 调设备硬件接口 在设备计费上报接口中 要更新加注升数 以及代理商分销余额 以及退款
        // 发送配置命令  在接收回复中发送出货指令
        doRunTerminal(terminal, config, discount);
        return AjaxResult.success(order);
    }

    /**
     * 退款
     *
     * @param vo 退款信息
     * @return 结果
     */
    @Override
    @Transactional
    public AjaxResult unifyRefund(PayVo vo) {
        PayInfo payInfo = payInfoService.getOne(Wrappers.lambdaQuery(PayInfo.class).eq(PayInfo::getOrderNo, vo.getOrderNo()));
        Order order = orderService.order(payInfo.getOrderNo());
        checkRefund(payInfo, order, vo);
        if (Objects.equals(order.getPayType(), WX_PAY)) {
            String res = doUniyRefund(vo, payInfo);
            JSONObject jsonObject = doCheckSignAndDecryptData(res);
            log.info("#############退款状态:{},21-退款中；22-退款成功；23-退款失败##################", jsonObject.getString("refundStatus"));
            return AjaxResult.success(jsonObject);
        }
        BigDecimal rebateAmount = sysUserService.updateRebateAmount(order.getAgentId(), order.getPayAmount().negate());
        Long customerId = order.getCustomerId();
        BigDecimal refundAmount = vo.getRefundAmount();
        BigDecimal totalRefund = getTotalRefund(order, vo.getRefundAmount());
        order.setRefundAmount(totalRefund);
        order.setRefundTime(LocalDateTime.now());
        order.setRebateAmount(order.getRebateAmount().add(rebateAmount));
        orderService.saveOrUpdate(order);
        customerAccountService.updateBlanceByCustomerId(customerId, refundAmount, order.getPayType().equals(FLEET_PAY));
        return AjaxResult.success();
    }

    private String doUniyRefund(PayVo vo, PayInfo payInfo) {
        String refundNo = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddhhmmssSSS"));
        BigDecimal refundAmount = vo.getRefundAmount().setScale(2, BigDecimal.ROUND_DOWN);
        orderRefundService.save(new OrderRefund(refundNo, vo.getOrderNo(), refundAmount, 0));
        JSONObject json = new JSONObject();
        // 外部订单号
        json.put("orderNo", payInfo.getOutPayNo());
        // 商户号
        json.put("merchantId", merchantId);
        // 订单号
        json.put("outRefundNo", refundNo);
        // 退款金额
//        json.put("refundAmount",order.getPayAmount().setScale(2,BigDecimal.ROUND_DOWN));
        json.put("refundAmount", refundAmount);
        // 退款成功回调地址
        json.put("notifyUrl", url.concat("api/wx/pay/unifyRefundSuccess"));
        String s = EncryptUtils.encryptData(json.toJSONString(), aesKey);
        String s1 = handelData(s);
        String sign = EncryptUtils.sign(s1, true);
        JSONObject param = JSONObject.parse(s1);
        param.put("sign", sign);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity(param.toJSONString(), headers);
        log.info("###########请求三方退款加密参数:{},原始参数:{}#################", param.toString(), json.toJSONString());
        // 发送请求
        String res = restTemplate.postForObject("https://eapi.lianfutech.cn/api/v1/refund/unifyRefund", entity, String.class);
        log.info("###########请求三方退款结果:{}#################", res);
        return res;
    }

    private void checkRefund(PayInfo payInfo, Order order, PayVo vo) {
        BigDecimal totalRefund = getTotalRefund(order, vo.getRefundAmount());
        if (vo.getRefundAmount().compareTo(BigDecimal.ZERO) < 0 || payInfo.getPayAmount().compareTo(totalRefund) < 0) {
            throw new ServiceException("退款金额不正确或者超出可退金额");
        }
        if (!Objects.equals(payInfo.getPayStatus(), SUCCESS_PAY)) {
            throw new ServiceException("此订单为支付成功，不允许退款");
        }
        if (Objects.equals(payInfo.getPayMethod(), BIZ_RECHARGE)) {
            BigDecimal rechargeAmount = getRechargeAmount(order);
            //对于充值赠送了金额这种情况必须全部一起退了
            if (rechargeAmount.compareTo(order.getPayAmount()) > 0 && vo.getRefundAmount().compareTo(order.getPayAmount()) != 0) {
                throw new ServiceException("此充值订单赠送了账户余额，请全额退款");
            }
            CustomerAccountVo customerAccount = customerAccountService.getByCustomerId(order.getCustomerId(), order.getPayType().equals(FLEET_PAY));
            if (customerAccount.getBalance().compareTo(rechargeAmount) < 0) {
                throw new ServiceException("账户余额不足抵扣");
            }
        }
    }

    private static BigDecimal getTotalRefund(Order order, BigDecimal refundAmount) {
        BigDecimal refunded = Objects.isNull(order.getRefundAmount()) ? BigDecimal.ZERO : order.getRefundAmount();
        BigDecimal totalRefund = refundAmount.add(refunded);
        return totalRefund;
    }

    private BigDecimal getRechargeAmount(Order order) {
        String orderDetail = order.getOrderDetail();
        if (StrUtil.isNotEmpty(orderDetail)) {
            RechargePackage rechargePackage = JSONObject.parseObject(orderDetail, RechargePackage.class);
            return rechargePackage.getRechargeAmount().add(rechargePackage.getGiftAmount());
        }
        return order.getPayAmount();
    }

    /**
     * 退款成功 回调通知
     *
     * @param json 信息
     * @return 结果
     */
    @Override
    @Transactional
    public JSONObject unifyRefundSuccess(JSONObject json) {
        log.info("############接收到退款通知:{}##############", json.toString());
        // 验签
        String sign = (String) json.remove("sign");
        EncryptUtils.checkSign(json.toString(), sign, true, publicKey);
        // 解密
        String data = EncryptUtils.decryptData(json.getString("data"), aesKey);
        log.info("############退款通知解密后参数:{}##################", data);
        JSONObject param = JSONObject.parse(data);
        // 更新訂單信息
        String refundNo = param.getString("outRefundNo");
        OrderRefund orderRefund = orderRefundService.getByRefundNo(refundNo);
        if (Objects.nonNull(orderRefund) && Objects.equals(orderRefund.getRefundStatus(), REFUND_SUCCESS)) {
            return getReturnInfoToWx();
        }
        String orderNo = orderRefund.getOrderNo();
        // 主动查询订单信息
        JSONObject jsonObject = queryRefundOrderStatusToWx(refundNo);
        Order order = orderService.getOne(Wrappers.lambdaQuery(Order.class).eq(Order::getOrderNo, orderNo));
        if (Objects.isNull(order)) {
            throw new ServiceException("订单不存在");
        }
        String refundStatus = jsonObject.getString("refundStatus");
        //退款状态;0处理中，1成功，2失败
        int status = refundStatus.equals("22") ? REFUND_SUCCESS : Objects.equals(refundStatus, "23") ? REFUND_FAIL : REFUND_INIT;
        orderRefund.setRefundStatus(status);
        orderRefund.setExtraInfo(json.toString());
        orderRefundService.saveOrUpdate(orderRefund);

        Order update = new Order();
        update.setId(order.getId());


        //订单状态：21-退款中；22-退款成功；23-退款失败
        if ("22".equals(refundStatus)) {
            BigDecimal refunded = Objects.isNull(order.getRefundAmount()) ? BigDecimal.ZERO : order.getRefundAmount();
            BigDecimal refundAmount = jsonObject.getBigDecimal("refundAmount");
            update.setRefundAmount(refundAmount.add(refunded));
            update.setRefundTime(jsonObject.getObject("refundTime", LocalDateTime.class));
            orderService.updateById(update);
            PayInfo payInfo = payInfoService.getOne(Wrappers.lambdaQuery(PayInfo.class).eq(PayInfo::getOrderNo, order.getOrderNo()));
            if (Objects.equals(payInfo.getPayMethod(), BIZ_RECHARGE)) {
                //部分退款修改账户余额
                BigDecimal rechargeAmount = refundAmount.compareTo(order.getPayAmount()) != 0 ? refundAmount : getRechargeAmount(order);
                accountService.updateBlanceByCustomerId(order.getCustomerId(), rechargeAmount.negate(), order.getPayType().equals(FLEET_PAY));
            }
        }
        if ("23".equals(refundStatus)) {
            log.info("###################订单:{}退款失败###################", orderNo);
        }
        // 更新详细信息
//        PayInfo payInfo = payInfoService.getOne(Wrappers.lambdaQuery(PayInfo.class).eq(PayInfo::getOrderNo, orderNo));
//        if (Objects.isNull(payInfo)){
//            throw new ServiceException("订单详细信息不存在");
//        }
        // todo 如果以后开发退款 需要判断呢如果是充值
//        if (StringUtils.equals(payInfo.getPayMethod(),String.valueOf(BusinessConstants.TRANSACTION_RECHARGE))){
        // 余额充值 然后加注了
//        }
//        PayInfo updatePay = new PayInfo();
//        updatePay.setId(payInfo.getId());
//        updatePay.setPayMethod(String.valueOf(BusinessConstants.TRANSACTION_REFUND));
//        payInfoService.updateById(updatePay);
        return getReturnInfoToWx();
    }

    /**
     * 查询微信订单退款状态
     *
     * @param refundNo 退款订单no
     * @return 响应信息
     */
    @Override
    public JSONObject queryRefundOrderStatusToWx(String refundNo) {
        JSONObject json = new JSONObject();
        json.put("outRefundNo", refundNo);
        json.put("merchantId", merchantId);
        String s = EncryptUtils.encryptData(json.toJSONString(), aesKey);
        String s1 = handelData(s);
        String sign = EncryptUtils.sign(s1, true);
        JSONObject param = JSONObject.parse(s1);
        param.put("sign", sign);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity(param.toJSONString(), headers);
        log.info("###########请求三方查询订单退款状态参数:{},原始参数:{}#################", param.toString(), json.toJSONString());
        // 发送请求
        String res = restTemplate.postForObject("https://eapi.lianfutech.cn/api/v1/refund/queryRefundOrder", entity, String.class);
        log.info("###########请求三方查询订单退款状态结果:{}#################", res);
        return doCheckSignAndDecryptData(res);
    }

    /**
     * 延迟订单解冻
     *
     * @param orderNo 订单id
     * @return 响应信息
     */
    @Override
    public JSONObject thawApply(String orderNo) {
        JSONObject json = new JSONObject();
        json.put("outWriteoffNo", orderNo);
        json.put("merchantId", merchantId);
        PayInfo payInfo = payInfoService.getOne(Wrappers.lambdaQuery(PayInfo.class).eq(PayInfo::getOrderNo, orderNo));
        json.put("amount", payInfo.getPayAmount());
        json.put("writeoffType", 0);
        json.put("orderNo", payInfo.getOutPayNo());
        String s = EncryptUtils.encryptData(json.toJSONString(), aesKey);
        String s1 = handelData(s);
        String sign = EncryptUtils.sign(s1, true);
        JSONObject param = JSONObject.parse(s1);
        param.put("sign", sign);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity(param.toJSONString(), headers);
        log.info("###########请求三方延迟订单解冻参数:{},原始参数:{}#################", param.toString(), json.toJSONString());
        // 发送请求
        String res = restTemplate.postForObject("https://papi.lianfutech.cn/v1/merchant/account/thawApply", entity, String.class);
        log.info("###########请求三方延迟订单解冻结果:{}#################", res);
        return doCheckSignAndDecryptData(res);
    }


    @Override
    public void cancelPay(OrderVo vo) {
        log.info("########{}#####", vo.toString());
        if (Objects.isNull(vo.getOrderId())) {
            throw new ServiceException("订单不可为空");
        }
        Order order = orderService.getById(vo.getOrderId());
        if (Objects.isNull(order)) {
            throw new ServiceException("订单不存在");
        }
        Order updateOrder = new Order();
        updateOrder.setId(order.getId());
        updateOrder.setStatus(ORDER_STATUS_CANCEL);
        orderService.updateById(updateOrder);
        payInfoService.update(Wrappers.lambdaUpdate(PayInfo.class)
                .eq(PayInfo::getOrderNo, order.getOrderNo()).set(PayInfo::getPayStatus, FAIL_PAY));
    }

    @Override
    public AjaxResult renew(PayVo vo) {
        if (Objects.isNull(vo.getTId())) {
            throw new ServiceException("终端id不能为空");
        }
        if (!Objects.equals(vo.getPayType(), WX_PAY)) {
            throw new ServiceException("仅支持微信支付");
        }
        Long userId = SecurityUtils.getUserId();
        BigDecimal renewFee = new BigDecimal(configService.selectConfigByKey("renew.fee.one.year"));
        String orderNo = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddhhmmssSSS"));
        Order order = new Order();
        saveOrderInfo(vo, renewFee, renewFee, userId, orderNo, WX_PAY, order, ORDER_STATUS_INIT);
        savePayInfo(renewFee, userId, orderNo, BIZ_RENEW, WAIT_PAY);

        // 调三方预支付
        OrderVo orderVo = new OrderVo();
        orderVo.setOrderNo(orderNo);
        orderVo.setPayAmount(renewFee);
        orderVo.setTerminalId(vo.getTId());

        JSONObject jsonObject = this.payToWx(orderVo);
        // 跟新外部订单号
        payInfoService.update(Wrappers.lambdaUpdate(PayInfo.class).eq(PayInfo::getOrderNo, orderNo)
                .set(PayInfo::getOutPayNo, jsonObject.getString("orderNo")));
        String miniPayInfo = jsonObject.getString("miniPayInfo");
        JSONObject result = JSONObject.parse(miniPayInfo);
        result.put("orderId", order.getId());
        return AjaxResult.success(result);
    }

    private void savePayInfo(BigDecimal payAmount, Long userId, String orderNo, Integer payMethod, Integer payStatus) {
        PayInfo payInfo = new PayInfo();
        payInfo.setPayMethod(payMethod);
        payInfo.setOrderNo(orderNo);
        payInfo.setRefUid(userId);
        payInfo.setPayAmount(payAmount);
        payInfo.setPayStatus(payStatus);
        payInfoService.save(payInfo);
    }

    private void saveOrderInfo(PayVo vo, BigDecimal amount, BigDecimal payAmount, Long userId, String orderNo,
                               Integer payType, Order order, Integer status) {
        order.setOrderNo(orderNo);
        order.setStatus(status);
        order.setPayType(payType);
        order.setCustomerId(userId);
        order.setTerminalId(vo.getTId());
        order.setAmount(amount);
        order.setPayAmount(payAmount);
        order.setPayTime(LocalDateTime.now());
        Terminal agentTerminal = terminalService.getOne(Wrappers.lambdaQuery(Terminal.class)
                .eq(Terminal::getId, vo.getTId()));
        if (Objects.nonNull(agentTerminal)) {
            order.setAgentId(agentTerminal.getAgentUid());
        }
        orderService.save(order);
    }

    private void saveAccountDetail(BigDecimal amount, Long accountId, BigDecimal newBalance, String orderNo, Long userId) {
        CustomerAccountDetail detail = new CustomerAccountDetail();
        detail.setRefAccount(accountId);
        // 支出
        detail.setIncome(EXPENDITURE_TYPE);
        detail.setRefUid(userId);
        // 客户
        detail.setCustomer(IS_CUSTOMER);
        detail.setAmount(amount);
        detail.setBalance(newBalance);
        // 加注
        detail.setType(BIZ_FILL);
        detail.setRefId(orderNo);
        accountDetailService.save(detail);
    }

    private JSONObject getReturnInfoToWx() {
        JSONObject result = new JSONObject();
        result.put("msg", "success");
        // 加密
        String s = EncryptUtils.encryptData("success", aesKey);
        result.put("data", s);
        result.put("code", "200");
        result.put("timestamp", String.valueOf(System.currentTimeMillis()));
        // 加签
        String sign1 = EncryptUtils.sign(result.toJSONString(), true);
        result.put("sign", sign1);
        return result;
    }

    /**
     * 拼装支付参数
     *
     * @param data 密文
     * @return 结果
     */
    private String handelData(String data) {
        JSONObject param = new JSONObject();
        param.put("partnerId", parentId);
        param.put("nonce", IdUtils.fastSimpleUUID().substring(0, 12));
        param.put("timestamp", System.currentTimeMillis());
        param.put("data", data);
        return param.toJSONString();
    }

    /**
     * 解析结果数据
     *
     * @param s 响应结果
     * @return 解析结果
     */
    private JSONObject parseResult(String s) {
        JSONObject jsonObject = JSONObject.parseObject(s);
        String code = jsonObject.getString("code");
        // 异常
        if (!StringUtils.equals(code, "0")) {
            throw new ServiceException("请求异常,请稍候重试!");
        }
        return jsonObject;
    }

    private JSONObject doCheckSignAndDecryptData(String s) {
        JSONObject jsonObject = parseResult(s);
        // 解签
        String oldSign = (String) jsonObject.remove("sign");
        EncryptUtils.checkSign(jsonObject.toJSONString(), oldSign, true, publicKey);
        // 解密
        String data1 = EncryptUtils.decryptData(jsonObject.getString("data"), aesKey);
        log.info("###########请求三方解密后结果:{}#################", data1);
        return JSONObject.parseObject(data1);
    }

    public static void main(String[] args) {
        JSONObject result = new JSONObject();
        result.put("msg", "success");
        // 加密
        String s = EncryptUtils.encryptData("success", "7B9BJCDEXPMC3QIWCYWXZEET4P7RN8ZX");

        String s1 = EncryptUtils.decryptData(s, "7B9BJCDEXPMC3QIWCYWXZEET4P7RN8ZX");
        System.out.println(s1);
    }
}
